package com.hang.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hang.config.RedisConfig;
import com.hang.entity.Message;
import com.hang.entity.Task;
import com.hang.exception.MyException;
import com.hang.exception.MyExceptionEnum;
import com.hang.handler.Websocket;
import com.hang.mapper.MessageMapper;
import com.hang.mapstruct.TaskMapStruct;
import com.hang.model.dto.request.*;
import com.hang.model.vo.CommentInfoVO;
import com.hang.model.vo.TaskInfoDetailVO;
import com.hang.model.vo.TaskInfoVO;
import com.hang.model.vo.TaskInfoVOList;
import com.hang.mongoEntity.FileUrlEntity;
import com.hang.mongoEntity.TaskEntity;
import com.hang.service.ICommentService;
import com.hang.service.ITaskService;
import com.hang.mapper.TaskMapper;
import lombok.AllArgsConstructor;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 *  任务管理服务类，核心功能区
 */
@Service
@AllArgsConstructor
public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task>
    implements ITaskService{

    private RedisConfig redisConfig;

    private TaskMapStruct taskMapStruct;

    private MessageMapper messageMapper;

    private ICommentService commentService;

    private MongoTemplate mongoTemplate;

    private Websocket websocket;
    /**
     * 发布任务
     * @param taskModelReq
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void publishTask(TaskModelReq taskModelReq) {
        Task task = taskMapStruct.taskModelReqToTask(taskModelReq);

        RedisTemplate<String, Object> redisTemplate = redisConfig.getRedisTemplateByDb(1);

        //提报人和接收人不能是同一人
        if(ObjectUtil.isNotNull(taskModelReq.getReceiveId()) && (taskModelReq.getCreateById().compareTo(taskModelReq.getReceiveId()) == 0)){
            throw new MyException(MyExceptionEnum.CREATE_RECEIVE_REPEAT);
        }

        //对文件存储到扩展类型中
        List<String> fileUrlList = Optional.ofNullable(taskModelReq.getFileUrlList()).orElse(new LinkedList<>());

        //随机生成mongoId
        String mongoId = IdWorker.getIdStr();

        //保存任务内容
        String content = taskModelReq.getContent();
        task.setMongoId(mongoId);

        mongoTemplate.save(new TaskEntity(mongoId,content));

        //保存任务附件
        if(!fileUrlList.isEmpty()){
            mongoTemplate.save(new FileUrlEntity(mongoId,fileUrlList));
        }

        //保存到Mysql数据库中
        save(task);

        //将发布的任务添加到redis中
        String key = taskModelReq.getItemId()+"-1";
        redisTemplate.opsForSet().add(key,task.getTaskId());

        //如果指定了接收人，那么生成相关消息并且发送给相关人
        Long receiveId = Optional.ofNullable(taskModelReq.getReceiveId()).orElse(null);

        if(ObjectUtil.isNotNull(receiveId)){
            sendMessage(task.getItemName(),receiveId,taskModelReq.getCreateBy());
        }
    }

    /**
     * 根据条件查询未被接单的任务
     * @param conditionDTO
     * @return
     */
    @Override
    public TaskInfoVOList getIncompleteTaskInHall(ItemConditionDTO conditionDTO) {
        Integer pageNum = conditionDTO.getPageNum();
        Integer pageSize = conditionDTO.getPageSize();

        Long itemId = conditionDTO.getItemId();
        String conditionName = Optional.ofNullable(conditionDTO.getCondition()).orElse("");

        QueryWrapper<Task> taskQueryWrapper = new QueryWrapper<>();

        taskQueryWrapper.lambda().isNull(Task::getReceiveId);
        taskQueryWrapper.lambda().eq(Task::getItemId,itemId);

        if(StrUtil.isNotBlank(conditionName)){
            taskQueryWrapper.and(
                    wrapper -> wrapper
                            .lambda()
                            .like(Task::getTaskName,conditionName)
                            .or()
                            .like(Task::getCreateBy,conditionName)
            );
        }

        taskQueryWrapper.lambda().orderByDesc(Task::getCreateTime);

        Page<Task> page = new Page<>(pageNum,pageSize);
        List<Task> taskList = Optional.ofNullable(this.baseMapper.selectPage(page, taskQueryWrapper).getRecords()).orElse(new LinkedList<>());

        Integer total = Optional.ofNullable(this.baseMapper.selectCount(taskQueryWrapper)).orElse(0);

        return packageTaskInfoVO(taskList,total);
    }

    /**
     * 按条件搜索我的任务
     *
     *  - 搜索条件
     *  - 排序方式
     *
     * @param taskConditionDTO
     * @return
     */
    @Override
    public TaskInfoVOList getTaskListByCondition(TaskConditionDTO taskConditionDTO) {
        //用户id
        Long userId = taskConditionDTO.getUserId();

        Long moduleId = taskConditionDTO.getModuleId();

        //分类方式
        Integer type = taskConditionDTO.getType();

        //搜索条件
        String condition = Optional.ofNullable(taskConditionDTO.getCondition()).orElse("");

        //任务类型
        Integer taskType = taskConditionDTO.getTaskType();
        //优先级
        Integer priority = taskConditionDTO.getPriority();

        //排序类型
        Integer sortType = taskConditionDTO.getSortType();
        //排序方式
        Integer sort = taskConditionDTO.getSort();

        //解决状态
        Integer solveStatus = taskConditionDTO.getSolveStatus();

        //页码，页距
        Integer pageNum = taskConditionDTO.getPageNum();
        Integer pageSize = taskConditionDTO.getPageSize();

        QueryWrapper<Task> taskQueryWrapper = new QueryWrapper<>();
        taskQueryWrapper.lambda().eq(Task::getModuleId,moduleId);

        Page<Task> page = new Page<>(pageNum,pageSize);

        if(type.compareTo(0) == 0){
            //全部
            taskQueryWrapper.and(
                    wrapper -> wrapper.lambda()
                            .eq(Task::getCreateById,userId)
                            .or()
                            .eq(Task::getReceiveId,userId)
            );
        }else if(type.compareTo(1) == 0){
            //我创建的
            taskQueryWrapper.lambda().eq(Task::getCreateById,userId);
        }else if(type.compareTo(2) == 0){
            //我处理的
            taskQueryWrapper.lambda().eq(Task::getReceiveId,userId);
        }

        //搜索条件查询
        if(StrUtil.isNotBlank(condition)){
            taskQueryWrapper.and(
                    wrapper -> wrapper.lambda()
                            .like(Task::getTaskName,condition)
                            .or()
                            .like(Task::getCreateBy,condition)
                            .or()
                            .like(Task::getReceiveName,condition)
            );
        }

        //任务类型
        if(taskType.compareTo(0) != 0){
            taskQueryWrapper.lambda().eq(Task::getTaskType,taskType);
        }

        //优先级
        if(priority.compareTo(0) != 0){
            taskQueryWrapper.lambda().eq(Task::getPriority,priority);
        }

        //解决状态
        if(solveStatus.compareTo(0) != 0){
            taskQueryWrapper.lambda().eq(Task::getSolveStatus,solveStatus);
        }

        if(sort.compareTo(1) == 0){
            if(sortType.compareTo(1) == 0){
                taskQueryWrapper.lambda().orderByDesc(Task::getUpdateTime);
            }else if(sortType.compareTo(2) == 0){
                taskQueryWrapper.lambda().orderByDesc(Task::getPriority);
            }
        }else{
            if(sortType.compareTo(1) == 0){
                taskQueryWrapper.lambda().orderByAsc(Task::getUpdateTime);
            }else if(sortType.compareTo(2) == 0){
                taskQueryWrapper.lambda().orderByAsc(Task::getPriority);
            }
        }

        List<Task> taskList = Optional.ofNullable(this.baseMapper.selectPage(page, taskQueryWrapper).getRecords()).orElse(new LinkedList<>());

        Integer total = Optional.ofNullable(this.baseMapper.selectCount(taskQueryWrapper)).orElse(0);
        return packageTaskInfoVO(taskList,total);
    }

    /**
     * 更新任务
     * @param taskModelReq
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTask(TaskModelReq taskModelReq) {
        Long taskId = taskModelReq.getTaskId();

        if(ObjectUtil.isNull(taskId)){
            throw new MyException(MyExceptionEnum.ID_IS_NULL);
        }

        //先查询出原有的数据
        Task dbTask = Optional.ofNullable(getById(taskId)).orElse(null);
        if(ObjectUtil.isEmpty(dbTask)){
            throw new MyException(MyExceptionEnum.DATA_DOES_NOT_EXIST);
        }

        //需要校验是否是本人操作
        if(dbTask.getCreateById().compareTo(taskModelReq.getCreateById()) != 0){
            throw new MyException(MyExceptionEnum.OPERATOR_UPDATE_EXCEPTION);
        }

        //提报人和接收人不能是同一人
        if(ObjectUtil.isNotNull(taskModelReq.getReceiveId()) && (taskModelReq.getCreateById().compareTo(taskModelReq.getReceiveId()) == 0)){
            throw new MyException(MyExceptionEnum.CREATE_RECEIVE_REPEAT);
        }

        String mongoId = Optional.ofNullable(dbTask.getMongoId()).orElse("");

        //去找相关mongoId中所对应的文档是否有内容需要更新
        Query query = new Query(Criteria.where("_id").is(mongoId));
        Update contentUpdate = new Update().set("content",taskModelReq.getContent());
        mongoTemplate.findAndModify(query,contentUpdate,TaskEntity.class);

        //查看链接是否需要更新
        List<String> fileList = Optional.ofNullable(taskModelReq.getFileUrlList()).orElse(new LinkedList<>());

        if(!fileList.isEmpty()){
            Update fileUpdate = new Update().set("fileUrlList",fileList);
            mongoTemplate.findAndModify(query,fileUpdate,FileUrlEntity.class);
        }

        //然后更新数据库中的内容
        Task task = taskMapStruct.taskModelReqToTask(taskModelReq);
        task.setTaskId(dbTask.getTaskId());

        updateById(task);

        //发送通知推送
        Long receiveId = Optional.ofNullable(taskModelReq.getReceiveId()).orElse(null);

        if(ObjectUtil.isNotNull(receiveId)){
            sendMessage(task.getItemName(),receiveId,taskModelReq.getCreateBy());
        }
    }

    /**
     * 批量删除任务
     * @param taskDeleteDTO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTaskList(TaskDeleteDTO taskDeleteDTO) {
        Long userId = taskDeleteDTO.getUserId();

        List<Long> taskIdList = taskDeleteDTO.getTaskIdList();

        if(ObjectUtil.isNull(userId)){
            throw new MyException(MyExceptionEnum.ID_IS_NULL);
        }

        if(ObjectUtil.isNull(taskIdList) || taskIdList.isEmpty()){
            throw new MyException(MyExceptionEnum.ONE_MORE_DELETE);
        }

        //校验是否存在不属于自己创建的任务
        List<Task> taskList = Optional.ofNullable(this.lambdaQuery().in(Task::getTaskId, taskIdList).list()).orElse(new LinkedList<>());

        Set<Long> distinctIdList = taskList.stream().map(Task::getCreateById).collect(Collectors.toSet());

        //判断集合中是否有不属于这个用户id，有则不允许删除
        distinctIdList.forEach(
                id ->{
                    if(id.compareTo(userId) != 0){
                        throw new MyException(MyExceptionEnum.OPERATOR_DELETE_EXCEPTION);
                    }
                }
        );

        //只删除mysql中的数据
        removeByIds(taskIdList);

        //移除redis中相关的任务
        RedisTemplate<String, Object> redisTemplate = redisConfig.getRedisTemplateByDb(1);
        taskList.forEach(
                task -> {
                    for (int i = 1; i <= 3; i++) {
                        String key = task.getItemId() + "-" + i;
                        if (redisTemplate.opsForSet().isMember(key,task.getTaskId())) {
                            redisTemplate.opsForSet().remove(key,task.getTaskId());
                        }
                    }
                }
        );

    }

    /**
     * 查询任务详情
     * @param taskId
     * @return
     */
    @Override
    public TaskInfoDetailVO getDetail(Long taskId) {
        if(ObjectUtil.isNull(taskId)){
            throw new MyException(MyExceptionEnum.ID_IS_NULL);
        }

        Task dbTask = Optional.ofNullable(getById(taskId)).orElse(null);

        if(ObjectUtil.isEmpty(dbTask)){
            throw new MyException(MyExceptionEnum.DATA_DOES_NOT_EXIST);
        }

        //查询任务的详情
        TaskInfoDetailVO taskInfoDetailVO = taskMapStruct.taskToTaskInfoDetailVO(dbTask);

        String mongoId = Optional.ofNullable(dbTask.getMongoId()).orElse("");

        if(StrUtil.isNotBlank(mongoId)){
            Query query = new Query(Criteria.where("_id").is(mongoId));
            TaskEntity taskEntity = Optional.ofNullable(mongoTemplate.findOne(query, TaskEntity.class)).orElse(new TaskEntity());

            taskInfoDetailVO.setContent(Optional.ofNullable(taskEntity.getContent()).orElse(""));

            FileUrlEntity fileUrlEntity = Optional.ofNullable(mongoTemplate.findOne(query, FileUrlEntity.class)).orElse(new FileUrlEntity());
            taskInfoDetailVO.setFileUrlList(Optional.ofNullable(fileUrlEntity.getFileUrlList()).orElse(new LinkedList<>()));
        }

        List<CommentInfoVO> commentInfoVOList = commentService.getCommonListInfo(taskId,2);
        taskInfoDetailVO.setCommentInfoVOList(commentInfoVOList);

        return taskInfoDetailVO;

    }

    /**
     * 修改任务状态[处理任务]
     * @param taskUpdateReq
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTaskStatus(TaskUpdateReq taskUpdateReq) {
        Long taskId = taskUpdateReq.getTaskId();
        Integer solveStatus = taskUpdateReq.getSolveStatus();
        Long receiveId = taskUpdateReq.getReceiveId();

        Task dbTask = Optional.ofNullable(getById(taskId)).orElse(null);

        if(ObjectUtil.isEmpty(dbTask)){
            throw new MyException(MyExceptionEnum.DATA_DOES_NOT_EXIST);
        }

        //校验是否是自己需要处理的任务
        Long userId = dbTask.getReceiveId();

        if(userId.compareTo(receiveId) != 0){
            throw new MyException(MyExceptionEnum.OPERATOR_TASK_EXCEPTION);
        }

        //记录旧的状态
        Integer oldSolveStatus = dbTask.getSolveStatus();

        //校验任务状态是否改变
        if(solveStatus.compareTo(oldSolveStatus) == 0){
            throw new MyException(MyExceptionEnum.TASK_STATUS_SAME);
        }

        //修改任务状态
        dbTask.setSolveStatus(solveStatus);

        //设置成当前时间
        LocalDateTime now = LocalDateTime.now();
        ZoneId zoneId = ZoneId.systemDefault();

        ZonedDateTime zonedDateTime = now.atZone(zoneId);
        Date date = Date.from(zonedDateTime.toInstant());

        dbTask.setSolveTime(date);
        updateById(dbTask);

        //redis修改相应的任务状态
        RedisTemplate<String, Object> redisTemplate = redisConfig.getRedisTemplateByDb(1);

        String oldKey = dbTask.getItemId() + "-" + oldSolveStatus;
        String newKey = dbTask.getItemId() + "-" + solveStatus;

        redisTemplate.opsForSet().remove(oldKey,taskId);
        redisTemplate.opsForSet().add(newKey,taskId);

        //发送消息通知
        sendUpdateMessage(dbTask.getItemName(),dbTask.getCreateById(),Optional.ofNullable(dbTask.getReceiveName()).orElse(""),dbTask.getTaskName());
    }

    /**
     * 任务接单
     * @param taskReceiveDTO
     */
    @Override
    public void receivingTask(TaskReceiveDTO taskReceiveDTO) {
        Long taskId = taskReceiveDTO.getTaskId();
        Long userId = taskReceiveDTO.getUserId();
        String realName = taskReceiveDTO.getRealName();

        //获取任务
        Task dbTask = Optional.ofNullable(getById(taskId)).orElse(null);

        if(ObjectUtil.isEmpty(dbTask)){
            throw new MyException(MyExceptionEnum.DATA_DOES_NOT_EXIST);
        }

        //校验任务接单人和提报人是否是同一个人
        if(userId.compareTo(dbTask.getCreateById()) == 0){
            throw new MyException(MyExceptionEnum.TASK_PERSON_SAME);
        }

        //接取任务
        dbTask.setReceiveId(userId);
        dbTask.setReceiveName(realName);

        updateById(dbTask);

        //发送消息给创建人
        sendReceiveMessage(dbTask.getItemName(),dbTask.getCreateById(),realName,dbTask.getTaskName());
    }

    /**
     * 查询解决时间为14天前的已完成的任务数据
     *
     * @return
     */
    @Override
    public List<Task> getFinishTaskIdList() {
        LocalDateTime passTime = LocalDateTime.now().plusDays(-14L);
        ZoneId zoneId = ZoneId.systemDefault();

        ZonedDateTime passZonedDateTime = passTime.atZone(zoneId);

        Date passDate = Date.from(passZonedDateTime.toInstant());

        return Optional.ofNullable(this.lambdaQuery().eq(Task::getSolveStatus, 3).lt(Task::getSolveTime, passDate).list()).orElse(new LinkedList<>());
    }

    private void sendReceiveMessage(String itemName,Long createById, String realName, String taskName) {
        //生成消息并发送
        Message message = new Message();
        message.setTitle("任务接取通知");
        message.setContent("您所在的项目【"+itemName+"】有新的消息:"+realName + "接取了您的任务，任务名为"+taskName+",可以留意您的任务完成情况");
        message.setReceiveId(createById);

        //推送通知
        try {
            websocket.sendMessage(message);
        } catch (IOException e) {
            log.error(e.toString());
        }

        //保存消息
        messageMapper.insert(message);
    }

    private void sendUpdateMessage(String itemName,Long createById,String receiveName,String taskName){
        //生成消息并发送
        Message message = new Message();
        message.setTitle("任务更新通知");
        message.setContent("您所在的项目【"+itemName+"】有新的消息:"+receiveName + "更新了您创建的任务状态，任务名为"+taskName+",请到您的任务列表中查看~");
        message.setReceiveId(createById);

        //推送通知
        try {
            websocket.sendMessage(message);
        } catch (IOException e) {
            log.error(e.toString());
        }

        //保存消息
        messageMapper.insert(message);
    }

    private void sendMessage(String itemName,Long receiveId,String createBy){
        //生成消息并发送
        Message message = new Message();
        message.setTitle("任务通知");
        message.setContent("您所在的项目【"+itemName+"】有新的消息:"+createBy + "给您提交了一个新的任务,任务内容请到您的任务列表中查看~");
        message.setReceiveId(receiveId);

        //推送通知
        try {
            websocket.sendMessage(message);
        } catch (IOException e) {
            log.error(e.toString());
        }

        //保存消息
        messageMapper.insert(message);
    }

    private TaskInfoVOList packageTaskInfoVO(List<Task> taskList,Integer count){
        TaskInfoVOList taskInfoListVO = new TaskInfoVOList();
        List<TaskInfoVO> taskInfoVOList = taskMapStruct.taskListToTaskInfoVOList(taskList);
        taskInfoListVO.setTotal(count);
        taskInfoListVO.setTaskInfoVOList(taskInfoVOList);

        return taskInfoListVO;
    }
}


